﻿using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace zijian666.Http;

/// <summary>
/// <seealso cref="HttpClient"/> 的扩展方法
/// </summary>
public static class HttpClientExtensions
{
    /// <summary>
    /// 默认超时时间(毫秒)
    /// </summary>
    public const int DEFAULT_TIMEOUT = 10000;
    // 一个空的字节数组
    private readonly static byte[] _emptyBytes = new byte[0];

    // 同步执行异步任务的策略
    private static ITaskSync _taskSync = TaskSync.Instance;

    // 捕获 {xxx} 占位符
    private readonly static Regex _stringTemplate = new(@"(?<=^|[^{])\{(\w+)\}(?=[^}]|$)", RegexOptions.Compiled);

    /// <summary>
    /// 设置同步发送策略
    /// </summary>
    /// <param name="action">0快速,1安全</param>
    /// <exception cref="NotSupportedException"><paramref name="action"/> not 0 or 1</exception>
    public static void SetSendSyncAction(int action)
    {
        _taskSync = action switch
        {
            0 => TaskSync.Instance,
            1 => TaskSyncSafe.Instance,
            _ => throw new NotSupportedException(),
        };
    }
    /// <summary>
    /// 将 <seealso cref="HttpClient"/> 或 <seealso cref="HttpMessageInvoker"/> 转为链式调用对象
    /// </summary>
    public static FluentHttpClient Fluent(this HttpMessageInvoker httpMessageInvoker)
        => new FluentHttpClient(httpMessageInvoker);

    /// <summary>
    /// 将 <seealso cref="HttpMessageInvoker"/> 转为链式调用对象
    /// </summary>
    public static FluentHttpClient Fluent(this HttpMessageHandler httpMessageHandler)
       => Fluent(new HttpMessageInvoker(httpMessageHandler));

    /// <summary>
    /// 设置请求的基础URL
    /// </summary>
    public static FluentHttpClient BaseUrl(this FluentHttpClient fluent, string url)
    {
        if (string.IsNullOrWhiteSpace(url))
        {
            return fluent;
        }
        url = url.Trim();
        if (!url.Contains("://"))
        {
            url = "http://" + url;
        }
        return fluent.BaseUrl(new Uri(url));
    }

    /// <summary>
    /// 设置请求的请求体
    /// </summary>
    public static FluentHttpClient Body(this FluentHttpClient fluent, string contentType, byte[] body)
    {
        var content = new ByteArrayContent(body ?? _emptyBytes);
        if (contentType is not null)
        {
            content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
        }
        return fluent.Body(content);
    }

    /// <summary>
    /// 设置请求的请求体
    /// </summary>
    public static FluentHttpClient Body(this FluentHttpClient fluent, string contentType, string data)
        => fluent.Body(MediaTypeHeaderValue.Parse(contentType), data);

    /// <summary>
    /// 设置请求的请求体
    /// </summary>
    public static FluentHttpClient Body(this FluentHttpClient fluent, MediaTypeHeaderValue type, string data)
    {
        var encoding = string.IsNullOrEmpty(type.CharSet) ? Encoding.UTF8 : Encoding.GetEncoding(type.CharSet);
        var content = new StringContent(data, encoding);
        content.Headers.ContentType = type;
        return fluent.Body(content);
    }

    /// <summary>
    /// 设置请求的请求体
    /// </summary>
    public static FluentHttpClient Body(this FluentHttpClient fluent, object data)
    {
        if (data is string s)
        {
            return fluent.Body(new StringContent(s, Encoding.UTF8));
        }
        var content = new FormUrlEncodedContent(KeyValuePraser.Enumerable(data));
        return fluent.Body(content);
    }

    /// <summary>
    /// 设置请求的表单内容, 用于 multipart/form-data 方式提交
    /// </summary>
    public static FluentHttpClient Form(this FluentHttpClient fluent, params FormData[] data)
    {
        var boundary = Guid.NewGuid().ToString("N");
        var multipart = new MultipartFormDataContent(boundary);
        foreach (var d in data)
        {
            multipart.Add(d.Value, d.Name);
        }
        multipart.Headers.Remove("Content-Type");
        multipart.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);
        return fluent.Body(multipart);
    }

    /// <summary>
    /// 设置请求的查询参数
    /// </summary>
    public static FluentHttpClient Query(this FluentHttpClient fluent, object data)
    {
        var content = new FormUrlEncodedContent(KeyValuePraser.Enumerable(data));
        return fluent.QueryString(content.ReadAsString());
    }

    /// <summary>
    /// 设置请求路径
    /// </summary>
    /// <param name="fluent"></param>
    /// <param name="pathTemplate">路径模版, 如: /user/{userid}</param>
    /// <param name="data">模版参数</param>
    /// <returns></returns>
    public static FluentHttpClient Path(this FluentHttpClient fluent, string pathTemplate, object data)
    {
        if (pathTemplate is null || !pathTemplate.Contains('{'))
        {
            return fluent.Path(pathTemplate);
        }
        var args = KeyValuePraser.Dictionary(data);
        var path = _stringTemplate.Replace(pathTemplate, m => args[m.Groups[1].Value]);
        return fluent.Path(path);

    }
    /// <summary>
    /// 添加默认请求头
    /// </summary>
    public static FluentHttpClient AddDefaultHeaders(this FluentHttpClient fluent)
    {
        return fluent.DefaultHeader("Accept", "*/*")
                    .DefaultHeader("Accept-Encoding", "gzip, deflate")
                    .DefaultHeader("Accept-Language", "zh-CN,zh;q=0.8");
    }

    #region Async

    /// <summary>
    /// 异步发送请求
    /// </summary>
    /// <param name="fluent"></param>
    /// <param name="timeoutMilliseconds">超时时间, 默认10秒</param>
    /// <returns></returns>
    public static Task<HttpResponseMessage> SendAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.SendAsync(new CancellationTokenSource(timeoutMilliseconds).Token);

    /// <summary>
    /// 异步发送 Get 请求
    /// </summary>
    public static Task<HttpResponseMessage> GetAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Get").SendAsync(timeoutMilliseconds);
    /// <summary>
    /// 异步发送 Post 请求
    /// </summary>
    public static Task<HttpResponseMessage> PostAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Post").SendAsync(timeoutMilliseconds);
    /// <summary>
    /// 异步发送 Put 请求
    /// </summary>
    public static Task<HttpResponseMessage> PutAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Put").SendAsync(timeoutMilliseconds);
    /// <summary>
    /// 异步发送 Delete 请求
    /// </summary>
    public static Task<HttpResponseMessage> DeleteAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Delete").SendAsync(timeoutMilliseconds);
    /// <summary>
    /// 异步发送 Head 请求
    /// </summary>
    public static Task<HttpResponseMessage> HeadAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Head").SendAsync(timeoutMilliseconds);
    /// <summary>
    /// 异步发送 Options 请求
    /// </summary>
    public static Task<HttpResponseMessage> OptionsAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Options").SendAsync(timeoutMilliseconds);
    /// <summary>
    /// 异步发送 Trace 请求
    /// </summary>
    public static Task<HttpResponseMessage> TraceAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Trace").SendAsync(timeoutMilliseconds);
    /// <summary>
    /// 异步发送 Patch 请求
    /// </summary>
    public static Task<HttpResponseMessage> PatchAsync(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Patch").SendAsync(timeoutMilliseconds);

    /// <summary>
    /// 异步发送 Get 请求, 并获取返回值字符串
    /// </summary>
    public static Task<string> GetStringAsync(this FluentHttpClient fluent)
        => fluent.Method("Get").SendAsync().ReadAsStringAsync();
    /// <summary>
    /// 异步发送 Post 请求, 并获取返回值字符串
    /// </summary>
    public static Task<string> PostStringAsync(this FluentHttpClient fluent)
        => fluent.Method("Post").SendAsync().ReadAsStringAsync();
    /// <summary>
    /// 异步发送 Put 请求, 并获取返回值字符串
    /// </summary>
    public static Task<string> PutStringAsync(this FluentHttpClient fluent)
        => fluent.Method("Put").SendAsync().ReadAsStringAsync();
    /// <summary>
    /// 异步发送 Delete 请求, 并获取返回值字符串
    /// </summary>
    public static Task<string> DeleteStringAsync(this FluentHttpClient fluent)
        => fluent.Method("Delete").SendAsync().ReadAsStringAsync();
    /// <summary>
    /// 异步发送 Patch 请求, 并获取返回值字符串
    /// </summary>
    public static Task<string> PatchStringAsync(this FluentHttpClient fluent)
        => fluent.Method("Patch").SendAsync().ReadAsStringAsync();


    /// <summary>
    /// 异步发送请求并获取返回值字符串
    /// </summary>
    public static Task<string> ReadAsStringAsync(this FluentHttpClient fluent)
        => fluent.SendAsync().ReadAsStringAsync();
    /// <summary>
    /// 异步从响应中获取返回值字符串
    /// </summary>
    public static async Task<string> ReadAsStringAsync(this Task<HttpResponseMessage> task)
    {
        var response = await task;
        response = response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
    /// <summary>
    /// 异步从响应中获取返回流
    /// </summary>
    public static async Task<Stream> ReadAsStreamAsync(this Task<HttpResponseMessage> task)
    {
        var response = await task;
        response = response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStreamAsync();
    }
    /// <summary>
    /// 异步从响应中获取返回字节
    /// </summary>
    public static async Task<byte[]> ReadAsByteArrayAsync(this Task<HttpResponseMessage> task)
    {
        var response = await task;
        response = response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsByteArrayAsync();
    }
    #endregion

    #region Sync


    /// <summary>
    /// 发送请求
    /// </summary>
    /// <param name="fluent"></param>
    /// <param name="timeoutMilliseconds">超时时间, 默认10秒</param>
    /// <returns></returns>
    public static HttpResponseMessage Send(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => _taskSync.RunSync(() => fluent.SendAsync(timeoutMilliseconds));

    /// <summary>
    /// 发送 Get 请求
    /// </summary>
    public static HttpResponseMessage Get(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Get").Send(timeoutMilliseconds);
    /// <summary>
    /// 发送 Post 请求
    /// </summary>
    public static HttpResponseMessage Post(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Post").Send(timeoutMilliseconds);
    /// <summary>
    /// 发送 Put 请求
    /// </summary>
    public static HttpResponseMessage Put(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Put").Send(timeoutMilliseconds);
    /// <summary>
    /// 发送 Delete 请求
    /// </summary>
    public static HttpResponseMessage Delete(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Delete").Send(timeoutMilliseconds);
    /// <summary>
    /// 发送 Head 请求
    /// </summary>
    public static HttpResponseMessage Head(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Head").Send(timeoutMilliseconds);
    /// <summary>
    /// 发送 Options 请求
    /// </summary>
    public static HttpResponseMessage Options(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Options").Send(timeoutMilliseconds);
    /// <summary>
    /// 发送 Trace 请求
    /// </summary>
    public static HttpResponseMessage Trace(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Trace").Send(timeoutMilliseconds);
    /// <summary>
    /// 发送 Patch 请求
    /// </summary>
    public static HttpResponseMessage Patch(this FluentHttpClient fluent, int timeoutMilliseconds = DEFAULT_TIMEOUT)
        => fluent.Method("Patch").Send(timeoutMilliseconds);

    /// <summary>
    /// 发送 Get 请求, 并获取返回值字符串
    /// </summary>
    public static string GetString(this FluentHttpClient fluent)
        => _taskSync.RunSync(() => fluent.GetStringAsync());
    /// <summary>
    /// 发送 Post 请求, 并获取返回值字符串
    /// </summary>
    public static string PostString(this FluentHttpClient fluent)
        => _taskSync.RunSync(() => fluent.PostStringAsync());
    /// <summary>
    /// 发送 Put 请求, 并获取返回值字符串
    /// </summary>
    public static string PutString(this FluentHttpClient fluent)
        => _taskSync.RunSync(() => fluent.PutStringAsync());
    /// <summary>
    /// 发送 Delete 请求, 并获取返回值字符串
    /// </summary>
    public static string DeleteString(this FluentHttpClient fluent)
        => _taskSync.RunSync(() => fluent.DeleteStringAsync());
    /// <summary>
    /// 发送 Patch 请求, 并获取返回值字符串
    /// </summary>
    public static string PatchString(this FluentHttpClient fluent)
        => _taskSync.RunSync(() => fluent.PatchStringAsync());

    /// <summary>
    /// 发送请求并获取返回值字符串
    /// </summary>
    public static string ReadAsString(this FluentHttpClient fluent)
       => _taskSync.RunSync(() => fluent.SendAsync().ReadAsStringAsync());

    /// <summary>
    /// 从响应中获取返回字符串
    /// </summary>
    public static string ReadAsString(this HttpResponseMessage response)
    {
        response = response.EnsureSuccessStatusCode();
        return _taskSync.RunSync(() => response.Content.ReadAsStringAsync());
    }
    /// <summary>
    /// 从响应中获取返回流
    /// </summary>
    public static Stream ReadAsStream(this HttpResponseMessage response)
    {
        response = response.EnsureSuccessStatusCode();
        return _taskSync.RunSync(() => response.Content.ReadAsStreamAsync());
    }
    /// <summary>
    /// 从响应中获取返回字节
    /// </summary>
    public static byte[] ReadAsByteArray(this HttpResponseMessage response)
    {
        response = response.EnsureSuccessStatusCode();
        return _taskSync.RunSync(() => response.Content.ReadAsByteArrayAsync());
    }

    /// <summary>
    /// 将 HTTP 内容序列化到字节数组
    /// </summary>
    public static byte[] ReadAsByteArray(this HttpContent content)
        => _taskSync.RunSync(content.ReadAsByteArrayAsync);

    /// <summary>
    /// 将 HTTP 内容序列化并返回将内容表示为流。
    /// </summary>
    public static Stream ReadAsStream(this HttpContent content)
        => _taskSync.RunSync(content.ReadAsStreamAsync);

    /// <summary>
    /// 将 HTTP 内容序列化到字符串
    /// </summary>
    public static string ReadAsString(this HttpContent content)
        => _taskSync.RunSync(content.ReadAsStringAsync);

    #endregion
}
